package com.stray.crawl.model.dao;


import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;

import com.stray.crawl.model.UrlParam;
import com.stray.crawl.model.PlanUrl;
import com.stray.crawl.supt.LRUCache;
import com.stray.crawl.utils.DataUtils;

public class UrlFormatter {
	
	private Map<PlanUrl, Pattern> urlPatternMap;
	
	/**
	 * ******* *
	 * Remarks *
	 * ******* *
	 * accessUrl stand for normal url for access. 
	 * 		Eg. http://forum3.hkgolden.com/topics.aspx?type=BW&page=2
	 * 
	 * paramUrl stand for parameterized url. 
	 * 		Eg. [http][domain:string].hkgolden.com/topics_[groupId:string].htm?[type:qstring][page:qnumber]
	 * 
	 * regexUrl stand for regular expression formatted url. 
	 * 		Eg. ^(?<protocol>https?://)(?<domain>[\\w-]+).hkgolden.com/topics_(?<category>[\\w-]+).htm(\\?)?(type=(?<type>[\\w-]+)&?)?(page=(?<page>\\d+)&?)?
	 */
	
	public UrlFormatter() {
		urlPatternMap = new LRUCache<PlanUrl, Pattern>();
	}
	
	public String extractHost(String url) {
		String str = DataUtils.substr(url, "://", "/");
		return DataUtils.substrAfter(str, ".");
	}

	public Pattern compilePattern(PlanUrl url) {
		Pattern pattern = null;
		
		if (!urlPatternMap.containsKey(url)) {
			String regexUrl = convertRegexUrl(url);
			pattern = Pattern.compile(regexUrl);
			
			urlPatternMap.put(url, pattern);
		} else {
			pattern = urlPatternMap.get(url);
		}
		
		return pattern;
	}
	
	public String convertRegexUrl(PlanUrl url) {
		String regexUrl;
		
		if (url.isConvertable()) {
			String paramUrl = convertParamUrl(url);
			
			paramUrl = paramUrl.replaceAll("\\?", "(\\\\?)?");
			StringBuffer buf = new StringBuffer();
			int start = -1, idx;
			
			while ((idx = paramUrl.indexOf('[', start)) > -1) {
				int idxTo = paramUrl.indexOf(']', idx);
				String param = paramUrl.substring(idx + 1, idxTo);
				
				String key = null;
				RegexPattern pattern = null;
				if (param.contains(":")) {
					String[] arr = param.split(":");
					
					key = arr[0];
					pattern = RegexPattern.valueOf(arr[1]);
				} else {
					pattern = RegexPattern.valueOf(param);
				}
				
				buf.append(paramUrl.substring(start + 1, idx));
				buf.append(pattern.getRegex(key));
				
				start = idxTo;
			}
			if (paramUrl.length() > start) {
				buf.append(paramUrl.substring(start + 1));
			}
			
			regexUrl = buf.toString();
		} else {
			regexUrl = url.getUrl();
		}
		
		return regexUrl;
	}
	
	public String convertParamUrl(PlanUrl url) {
		StringBuffer buf = new StringBuffer();
		
		List<UrlParam> params = url.getParams();

		if (params != null && !params.isEmpty()) {
			Collections.sort(params);
			
			for (UrlParam p : params) {
				String key = p.getKey();
				String type = p.getType();

				if ("R".equals(p.getAsRequestOrContent())) {
					buf.append("[")
					   .append(key)
					   .append(":")
					   .append(type)
					   .append(p.isOptional() ? "_query_optional" : "_query")
					   .append("]");
				}
			}
		}
		
		buf.insert(0, url.getUrl() + (buf.length() > 0 ? "?" : ""));
		
		return buf.toString();
	}


	public String sortAccessUrlOrder(String url) {
		int fmIdx = url.indexOf('?');
		int toIdx = url.indexOf('#', fmIdx);
		
		fmIdx = fmIdx >= 0 ? fmIdx + 1: 0;
		toIdx = toIdx >= 0 ? toIdx : url.length();
		
		String querystr = url.substring(fmIdx, toIdx);
		String[] params = querystr.split("&");
		Arrays.sort(params);

		return url.substring(0, fmIdx) + DataUtils.joins(params, "&") + url.substring(toIdx);
	}

	/*
	public Map<String, String> extractParams(URL url) {
		String[] split = url.getQuery().split("&");
		HashMap<String, String> params = new HashMap<>();
		
		if (split != null) {
			for (String s : split) {
				String key = null, val = "";
				int idx = s.indexOf('=');
				
				if (idx >= 0) {
					key = s.substring(0, idx);
					val = s.substring(idx + 1, s.length());
				} else {
					key = s; 
				}
				
				params.put(key, val);
			}
		}
		
		return params;
	}
	
	public String convertParamUrl(String url, Map<String, String> params) {
		StringBuffer buf = new StringBuffer();
		
		for (Entry<String, String> entry : params.entrySet()) {
			if (buf.length() == 0) {
				buf.append("?");
			} else {
				buf.append("&");
			}
			buf.append(entry.getKey() + "=" + entry.getValue());
		}
		
		return url + buf.toString();
	}
	
	public Map<String, String> extractParams(String paramUrl, String accessUrl) {
		int start = -1, idx;
		Map<String, String> params = new HashMap<>();
		
		Pattern pattern = convertRegexUrlPattern(paramUrl);
		Matcher matcher = pattern.matcher(accessUrl);
		
		if (matcher.matches()) {
			while ((idx = paramUrl.indexOf("[", start)) > -1) {
				int idxTo = paramUrl.indexOf("]", idx);
				String param = paramUrl.substring(idx + 1, idxTo);
				
				if (param.contains(":")) {
					String key = Utils.substrBefore(param, ":");
					params.put(key, matcher.group(key));
				}
				
				start = idxTo;
			}
		}
		
		return params;
	}
	
	// TODO to be review
	protected String convertAccessUrl(String paramUrl, Map<String, String> params) {
		StringBuffer buf = new StringBuffer();
		boolean hasQueryParam = false;
		int start = -1, idx;
		
		while ((idx = paramUrl.indexOf("[", start)) > -1) {
			int idxTo = paramUrl.indexOf("]", idx);
			String param = paramUrl.substring(idx + 1, idxTo);
			
			buf.append(paramUrl.substring(start + 1, idx));
			
			// Parse Key, Type, Value
			String key = null;
			RegexPattern pattern = null;
			if (param.contains(":")) {
				String[] arr = param.split(":");
				
				key = arr[0];
				pattern = RegexPattern.valueOf(arr[1]);
			} else {
				pattern = RegexPattern.valueOf(param);
			}

			// Add join char
			if (pattern.isQuery()) {
				if (hasQueryParam) {
					buf.append("&");
				}
				hasQueryParam = true;
			}
			
			buf.append(pattern.getReplace(key, params.get(key)));
			
			start = idxTo;
		}
		if (paramUrl.length() > start) {
			buf.append(paramUrl.substring(start + 1));
		}
		
		return buf.toString();
	}
	*/

}